﻿/*
 * Created by SharpDevelop.
 * User: thanh.nguyenchi
 * Date: 5/9/2012
 * Time: 4:38 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using VDataAdapter.BlockingQueue;
using System.Collections;
using System.Collections.Generic; 
using log4net;
using VDataAdapter.ClientManagement;

namespace VDataAdapter.ClientServer
{
    public delegate void JavaServerResetHandler();

    /// <summary>
    /// Description of ClientThread.
    /// </summary>
    public class ClientThread
    {
        protected TcpClient clientSocket;
        protected NetworkStream clientStream;
        protected bool isRunning;
        protected int iSeq;
        protected CommunicationDataQueue qMain;
        // protected ArrayList memHistories;
        protected MessageHistories _messageHistories;
        protected readonly ILog _log = LogManager.GetLogger(typeof(ClientThread));
        protected static int seqTemp;
        public static int SeqTemp
        {
            get { return seqTemp; }
        }
        protected VndDequeue dequeuer;

        private JavaServerResetHandler _clientResetHandler;

        public ClientThread(TcpClient clientSocket, CommunicationDataQueue qMain, ArrayList memHistories)
        {
            dequeuer = new VndDequeue(qMain, "test.txt");
            this.clientSocket = clientSocket;
            this.qMain = qMain;
            setConfigTcpClient();
            this.clientStream = this.clientSocket.GetStream();
            this.clientStream.WriteTimeout = 2000;
            // this.memHistories = memHistories;
            this._messageHistories = new MessageHistories(memHistories, 10000, 1000);
            if (this.clientStream != null)
            {
                iSeq = 1;
                isRunning = false;
            }
        }

        public void setConfigTcpClient()
        {
            this.clientSocket.NoDelay = true;
            this.clientSocket.SendTimeout = 0;
        }

        protected virtual void HandleResetSequence()
        {
            _log.Info("Received seq = 1");
        }


        protected virtual void HandleRecoverSequence()
        {
            _log.Info("Handle Recover Sequence");
        }

        protected virtual void HandleMissingSequence(int seq)
        {
            string msg;
            if (seq < _messageHistories.Count)
            {
                for (int j = seq; j < _messageHistories.Count; j++)
                {
                    msg = _messageHistories.GetMessage(j);
                    int i = sendMsg(iSeq.ToString() + msg);
                    iSeq++;
                }
            }
        }

        public virtual void Start()
        {
            string clientName = clientSocket.Client.RemoteEndPoint.ToString();
            _log.Info("Start serving client: " + clientName);
             
            // Receive msg start from client and process
            String strReciveMsg = recvMsg();
            if (strReciveMsg.Length > 0)
            {
                // Process msg
                _log.Info("Rev:" + strReciveMsg);
                string[] sTemp = strReciveMsg.Split('|');
                if (sTemp[0].Equals("start"))
                {
                    // Parse msg and recovery from client seq to current seq
                    int seq = int.Parse(sTemp[1]);
                    // Neu seq = 1 thi clear queue va mem
                    if (seq == 1)
                    {
                        this.qMain.clear();
                        this._messageHistories.Clear();
                        // Gui du lieu load data
                        // BEGIN
                        HandleResetSequence();
                        // END
                    }
                    if (seq >= iSeq)
                    {
                        iSeq = seq;
                        _log.Info("[>]Send data to client");
                        int i = sendMsg(seq.ToString() + "#GA#Connected to server....#0" + clientSocket.GetHashCode());
                        _log.Info("[Length] = " + i.ToString() + "  [Content] = " + iSeq.ToString() + "#GA#Connected to server....#0");
                        HandleRecoverSequence();
                    }
                    else
                    {
                        HandleMissingSequence(seq);
                    }
                    isRunning = true;
                }
                else
                {
                    Stop();
                }
            }

            _log.Info("Start sending data to client: " + clientName);

            while (isRunning)
            {
                try
                {
                    // Nhan du lieu tu queue and send to client
                    CommunicationData obj = null;
                    obj = qMain.get();
                    if (obj != null)
                    {
                        if (isRunning == true)
                        {
                            BaseObject objBaseObject = obj.getBaseObjectData();
                            DataQueue objDataQueue = obj.getDataQueue();
                            string sClassName = objBaseObject.getObjectType();
                            MessageObject objMessage = (MessageObject)objBaseObject;
                            String sMessage = objMessage.getMsg();
                            iSeq++;
                            int i = sendMsg(iSeq.ToString() + "#" + sMessage);
                            _log.DebugFormat("Send Msg {0}", sMessage);

                            // Update du lieu da gui vao memmory
                            _messageHistories.Add(sMessage);
                            //if (iSeq == 1000)
                            //{
                            //    isRunning = false;
                            //    Stop();
                            //    break;
                            //}
                        }
                    }
                    else
                    {
                        Thread.Sleep(10);
                    }
                }
                catch (Exception ex)
                {
                    isRunning = false;
                    _log.Error("Start Error", ex);
                    Stop();
                    break;
                }
            }

            _log.Info("Stop serving client: " + clientName);
        }

        public int Stop()
        {
            _log.Info("ClientQueueServer stopped from error.");
            //Close all connection
            this.clientStream.Close();
            this.clientSocket.Close();

            _log.Info("Reset client.");
            //Notify current client to restart
            if (_clientResetHandler != null)
            {
                _clientResetHandler();
            }

            return 1;
        }

        public bool IsRunning()
        {
            return isRunning;
        }
        public int sendMsg(string msg)
        {
            try
            {
                byte[] clientData = new byte[1024];
                clientData = Encoding.UTF8.GetBytes(msg);

                int i = clientData.Length;
                byte[] abyte0 = new byte[i + 2];
                abyte0[0] = (byte)(i >> 8 & 0xff);
                abyte0[1] = (byte)(i & 0xff);

                Array.Copy(clientData, 0, abyte0, 2, i);
                clientStream.Write(abyte0, 0, abyte0.Length);
                clientStream.Flush();
                int hashcode = clientSocket.GetHashCode();
                
                // dequeuer.AddToFile("queue count: " + qMain.que.Count + "hashcode: " + hashcode + " " + msg);
                //_log.Warn(msg);
                return msg.Length;
            }
            catch (Exception e)
            {
                isRunning = false;
                _log.Error("sendMsg Error : " + msg, e);
                Stop();
                return -1;
            }
        }

        public String recvMsg()
        {
            try
            {
                byte[] abyte0 = recvBytes();
                System.Text.Encoding enc = Encoding.UTF8;
                string s = enc.GetString(abyte0);
                return s;
            }
            catch (Exception e)
            {
                isRunning = false;
                _log.Error("recvMsg ", e);
                Stop();
                return "";
            }
        }

        byte[] recvBytes()
        {
            byte[] abyte0 = read(2);
            if (abyte0 == null)
                return null;
            int i = ((abyte0[0] & 0xff) << 8) + (abyte0[1] & 0xff);
            if (i <= 0)
            {
                return null;
            }
            else
            {
                return read(i);
            }
        }
        byte[] read(int i)
        {
            byte[] abyte0 = new byte[i];
            int j = 0;
            int l = 0;
            for (int k = i; k > 0; k -= l)
            {
                byte[] abyte1 = new byte[i];
                l = clientStream.Read(abyte1, 0, k);
                if (l == -1)
                {
                    return null;
                }
                Array.Copy(abyte1, 0, abyte0, j, l);
                j += l;
            }
            return abyte0;
        }
        
        //Accept the current client to notify of current problem
        public void acceptResetHandler(JavaServerResetHandler handler)
        {
            _clientResetHandler = handler;
        }
    }
}
